"""
场景 JSON Schema 验证器
用于验证 scence_text.json 的结构和内容是否符合规范
"""

from typing import Any, Dict, List, Optional, Union
import re


class ScenesSchemaValidator:
    """场景 JSON Schema 验证器"""

    # 支持的视觉元素类型
    VISUAL_TYPES = {"text", "formula", "code", "chart", "shape", "image"}

    # 支持的效果类型
    EFFECT_TYPES = {
        "fade_in", "fade_out", "write", "create", "transform",
        "move_to", "scale_to", "rotate_to", "color_change"
    }

    # 支持的位置类型
    POSITION_TYPES = {
        "center", "top", "bottom", "left", "right",
        "top_left", "top_right", "bottom_left", "bottom_right"
    }

    # 支持的主题类型
    THEME_TYPES = {"dark", "light", "default"}

    def __init__(self, strict: bool = True):
        """
        初始化验证器

        Args:
            strict: 是否严格模式，严格模式下会验证更多细节
        """
        self.strict = strict
        self.errors: List[str] = []

    def validate(self, data: Dict[str, Any]) -> tuple[bool, List[str]]:
        """
        验证场景数据

        Args:
            data: 要验证的数据

        Returns:
            (是否有效, 错误列表)
        """
        self.errors = []

        # 验证顶层结构
        self._validate_top_level(data)

        # 验证 meta
        if "meta" in data:
            self._validate_meta(data["meta"])

        # 验证 scenes
        if "scenes" in data:
            self._validate_scenes(data["scenes"])
        else:
            self.errors.append("缺少 'scenes' 字段")

        return len(self.errors) == 0, self.errors

    def _validate_top_level(self, data: Dict[str, Any]) -> None:
        """验证顶层结构"""
        if "version" not in data:
            self.errors.append("缺少 'version' 字段")
        elif not isinstance(data["version"], str):
            self.errors.append("'version' 必须是字符串")
        elif not re.match(r"^\d+\.\d+$", data["version"]):
            self.errors.append("'version' 格式应为 X.Y")

    def _validate_meta(self, meta: Dict[str, Any]) -> None:
        """验证 meta 字段"""
        # title
        if "title" in meta:
            if not isinstance(meta["title"], str):
                self.errors.append("'meta.title' 必须是字符串")
            elif not meta["title"].strip():
                self.errors.append("'meta.title' 不能为空")

        # language
        if "language" in meta:
            if not isinstance(meta["language"], str):
                self.errors.append("'meta.language' 必须是字符串")
            # 简单的语言代码验证
            elif not re.match(r"^[a-z]{2}(-[A-Z]{2})?$", meta["language"]):
                self.errors.append("'meta.language' 格式不正确")

        # voice
        if "voice" in meta:
            if not isinstance(meta["voice"], str):
                self.errors.append("'meta.voice' 必须是字符串")

        # fps
        if "fps" in meta:
            if not isinstance(meta["fps"], int):
                self.errors.append("'meta.fps' 必须是整数")
            elif meta["fps"] <= 0 or meta["fps"] > 120:
                self.errors.append("'meta.fps' 必须在 1-120 之间")

        # resolution
        if "resolution" in meta:
            if not isinstance(meta["resolution"], str):
                self.errors.append("'meta.resolution' 必须是字符串")
            elif not re.match(r"^\d+x\d+$", meta["resolution"]):
                self.errors.append("'meta.resolution' 格式应为 WxH")

        # style/theme
        if "style" in meta:
            if not isinstance(meta["style"], str):
                self.errors.append("'meta.style' 必须是字符串")
            elif meta["style"] not in self.THEME_TYPES:
                self.errors.append(f"'meta.style' 必须是 {self.THEME_TYPES} 中的一个")

    def _validate_scenes(self, scenes: Any) -> None:
        """验证 scenes 数组"""
        if not isinstance(scenes, list):
            self.errors.append("'scenes' 必须是数组")
            return

        if len(scenes) == 0:
            self.errors.append("'scenes' 不能为空")
            return

        for i, scene in enumerate(scenes):
            if not isinstance(scene, dict):
                self.errors.append(f"scene[{i}] 必须是对象")
                continue

            self._validate_scene(scene, i)

    def _validate_scene(self, scene: Dict[str, Any], index: int) -> None:
        """验证单个场景"""
        prefix = f"scene[{index}]"

        # id
        if "id" in scene:
            if not isinstance(scene["id"], str):
                self.errors.append(f"{prefix}.id 必须是字符串")
            elif not re.match(r"^scene-\d{3}$", scene["id"]):
                self.errors.append(f"{prefix}.id 格式应为 scene-XXX")

        # title
        if "title" in scene:
            if not isinstance(scene["title"], str):
                self.errors.append(f"{prefix}.title 必须是字符串")

        # narration
        if "narration" in scene:
            if not isinstance(scene["narration"], str):
                self.errors.append(f"{prefix}.narration 必须是字符串")
            elif self.strict and not scene["narration"].strip():
                self.errors.append(f"{prefix}.narration 不能为空")
        else:
            self.errors.append(f"{prefix} 缺少 'narration' 字段")

        # duration_hint_sec
        if "duration_hint_sec" in scene:
            if not isinstance(scene["duration_hint_sec"], (int, float)):
                self.errors.append(f"{prefix}.duration_hint_sec 必须是数字")
            elif scene["duration_hint_sec"] <= 0:
                self.errors.append(f"{prefix}.duration_hint_sec 必须大于 0")
            elif self.strict and scene["duration_hint_sec"] > 60:
                self.errors.append(f"{prefix}.duration_hint_sec 建议不超过 60 秒")

        # visuals
        if "visuals" in scene:
            self._validate_visuals(scene["visuals"], prefix)

        # notes
        if "notes" in scene:
            if not isinstance(scene["notes"], str):
                self.errors.append(f"{prefix}.notes 必须是字符串")

    def _validate_visuals(self, visuals: Any, prefix: str) -> None:
        """验证 visuals 数组"""
        if not isinstance(visuals, list):
            self.errors.append(f"{prefix}.visuals 必须是数组")
            return

        for j, visual in enumerate(visuals):
            if not isinstance(visual, dict):
                self.errors.append(f"{prefix}.visuals[{j}] 必须是对象")
                continue

            v_prefix = f"{prefix}.visuals[{j}]"

            # type
            if "type" in visual:
                if not isinstance(visual["type"], str):
                    self.errors.append(f"{v_prefix}.type 必须是字符串")
                elif visual["type"] not in self.VISUAL_TYPES:
                    self.errors.append(
                        f"{v_prefix}.type 必须是 {self.VISUAL_TYPES} 中的一个"
                    )

            # content
            if "content" in visual:
                if not isinstance(visual["content"], str):
                    self.errors.append(f"{v_prefix}.content 必须是字符串")

            # position
            if "position" in visual:
                if not isinstance(visual["position"], str):
                    self.errors.append(f"{v_prefix}.position 必须是字符串")
                elif visual["position"] not in self.POSITION_TYPES:
                    self.errors.append(
                        f"{v_prefix}.position 必须是 {self.POSITION_TYPES} 中的一个"
                    )

            # effects
            if "effects" in visual:
                if not isinstance(visual["effects"], list):
                    self.errors.append(f"{v_prefix}.effects 必须是数组")
                else:
                    for k, effect in enumerate(visual["effects"]):
                        if not isinstance(effect, str):
                            self.errors.append(
                                f"{v_prefix}.effects[{k}] 必须是字符串"
                            )
                        elif effect not in self.EFFECT_TYPES:
                            self.errors.append(
                                f"{v_prefix}.effects[{k}] 必须是 {self.EFFECT_TYPES} 中的一个"
                            )


def validate_scenes_json(data: Dict[str, Any], strict: bool = True) -> tuple[bool, List[str]]:
    """
    验证场景 JSON 数据

    Args:
        data: 要验证的数据
        strict: 是否严格模式

    Returns:
        (是否有效, 错误列表)
    """
    validator = ScenesSchemaValidator(strict=strict)
    return validator.validate(data)


def validate_scenes_file(file_path: Union[str, Any], strict: bool = True) -> tuple[bool, List[str]]:
    """
    验证场景 JSON 文件

    Args:
        file_path: 文件路径
        strict: 是否严格模式

    Returns:
        (是否有效, 错误列表)
    """
    import json
    from pathlib import Path

    path = Path(file_path)
    if not path.exists():
        return False, [f"文件不存在: {file_path}"]

    try:
        with open(path, "r", encoding="utf-8") as f:
            data = json.load(f)
    except json.JSONDecodeError as e:
        return False, [f"JSON 解析错误: {e}"]
    except Exception as e:
        return False, [f"文件读取错误: {e}"]

    return validate_scenes_json(data, strict=strict)


if __name__ == "__main__":
    # 测试代码
    import sys

    if len(sys.argv) < 2:
        print("用法: python scenes_schema.py <json_file>")
        sys.exit(1)

    file_path = sys.argv[1]
    is_valid, errors = validate_scenes_file(file_path, strict=True)

    if is_valid:
        print(f"✓ {file_path} 验证通过")
        sys.exit(0)
    else:
        print(f"✗ {file_path} 验证失败:")
        for error in errors:
            print(f"  - {error}")
        sys.exit(1)
